home *** CD-ROM | disk | FTP | other *** search
/ QuickTime 2.0 Developer Kit / QuickTime 2.0 Developer Kit.iso / mac / MAC / Programming Stuff / Documentation / develop / develop Issue 20 / develop 20 code / Sound Components / NoiseMaker / NoiseMaker.c next >
Encoding:
C/C++ Source or Header  |  1994-09-08  |  39.5 KB  |  1,150 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        NoiseMaker.c
  3.  
  4.     Contains:    Sample sound output component
  5.  
  6.     Written by:    Kip Olson
  7.  
  8.     Copyright:    © 1994 by Apple Computer, Inc.
  9. */
  10.  
  11. #include <Memory.h>
  12. #include <Errors.h>
  13. #include <SoundInput.h>
  14. #include <Components.h>
  15. #include <GestaltEqu.h>
  16.  
  17. #include "Sound.h"
  18. #include "SoundComponents.h"
  19.  
  20. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  21. // Constants
  22. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  23.  
  24. #define kDelegateComponentCall            ((ComponentRoutine) -1L)    // flag that selector should be delegated instead of called
  25.  
  26. #define kNoiseMakerVersion                0x00010000        // version for this sound component
  27. #define kRequiredSndMgrMajorRev            3                // Sound Manager version required to run this component
  28.  
  29. // hardware settings
  30.  
  31. #define kSampleSizesCount                2
  32. #define     k8BitSamples                8                // 8-bit samples
  33. #define     k16BitSamples                16                // 16-bit samples
  34.  
  35. #define kSampleRatesCount                3
  36. #define     kSupportsSampleRateRange    false            // set to true to use sample rate range
  37. #define     kSampleRateMin                0x2B110000        // sample rate min = 11.025 kHz
  38. #define     kSampleRateMax                0xAC440000        // sample rate max = 44.100 kHz
  39.  
  40. #define     kSampleRate44                0xAC440000        // 44.1000 kHz rate
  41. #define     kSampleRate22                0x56220000        // 22.050 kHz rate
  42. #define     kSampleRate11                0x2B110000        // 11.025 kHz rate
  43.  
  44. #define kChannelsCount                    2
  45. #define     kNumChannelsMono            1                // mono
  46. #define     kNumChannelsStereo            2                // stereo
  47.  
  48. #define kInterruptBufferSamples            512                // size of interrupt buffer in samples
  49.  
  50. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  51. // Data Structures
  52. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  53.  
  54. /* Data structure passed to some GetInfo calls */
  55.  
  56. typedef struct {
  57.     short    count;
  58.     Handle    handle;
  59. } HandleList, *HandleListPtr;
  60.  
  61. /* Preferences data structure. A handle containing this data structure is stored in the
  62.    Sound Preferences file and is loaded into the sound component refcon when it is opened. */
  63.  
  64. typedef struct {
  65.     UnsignedFixed        sampleRate;
  66.     short                sampleSize;
  67.     short                numChannels;
  68.     unsigned long        volume;
  69. } PreferencesRecord, *PreferencesPtr, **PreferencesHandle;
  70.  
  71. /* Sound component globals */
  72.  
  73. typedef struct {
  74.  
  75. // these are general purpose variables that every sound component will need
  76.     ComponentInstance        self;                    // ourselves
  77.     ComponentInstance        sourceComponent;        // component to call when hardware needs more data
  78.     SoundComponentDataPtr    sourceDataPtr;            // pointer to source data structure
  79.     Handle                    globalsHandle;            // handle to component globals
  80.     Boolean                    inSystemHeap;            // true if component loaded in system heap, false if in app heap
  81.     Boolean                    prefsChanged;            // true if preferences have changed
  82.     PreferencesHandle        prefsHandle;            // global preferences
  83.  
  84. // these are variables specific to this implementation
  85.     SoundComponentData        hwSettings;                // current hardware settings
  86.     unsigned long            hwVolume;                // current hardware volume
  87.     Boolean                    hwInterruptsOn;            // true if sound is playing
  88.     Boolean                    hwInitialized;            // true if hardware was initialized by __InitOutputDevice 
  89.     SndChannelPtr            sndChan;                // sound channel in use
  90. }
  91.  GlobalsRecord, *GlobalsPtr;
  92.  
  93. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  94. // Prototypes
  95. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  96.  
  97. #ifdef THINK_C
  98. pascal ComponentResult    main(ComponentParameters *params, GlobalsPtr globals);
  99. #else
  100. pascal ComponentResult    NoiseMaker(ComponentParameters *params, GlobalsPtr globals);
  101. #endif
  102.  
  103. ComponentRoutine        GetComponentRoutine(short selector);
  104. pascal ComponentResult    __ComponentRegister(void *unused1);
  105. pascal ComponentResult    __ComponentVersion(void *unused1);
  106. pascal ComponentResult    __ComponentCanDo(void *unused1, short selector);
  107. pascal ComponentResult    __ComponentOpen(void *unused1, ComponentInstance self);
  108. pascal ComponentResult    __ComponentClose(GlobalsPtr globals, ComponentInstance self);
  109. pascal ComponentResult    __InitOutputDevice(GlobalsPtr globals, long actions);
  110. pascal ComponentResult    __GetInfo(GlobalsPtr globals, SoundSource sourceID, OSType selector, void *infoPtr);
  111. pascal ComponentResult    __SetInfo(GlobalsPtr globals, SoundSource sourceID, OSType selector, void *infoPtr);
  112. pascal ComponentResult    __StartSource(GlobalsPtr globals, short count, SoundSource *sources);
  113. pascal ComponentResult    __PlaySourceBuffer(GlobalsPtr globals, SoundSource sourceID, SoundParamBlockPtr pb, long actions);
  114.  
  115. Handle                    NewHandleLockClear(long len, Boolean inSystemHeap);
  116. PreferencesHandle        GetPreferences(ComponentInstance self, Boolean inSystemHeap);
  117. void                    SavePreferences(ComponentInstance self, PreferencesHandle prefsHandle);
  118. OSErr                    SetupHardware(GlobalsPtr globals);
  119. void                    ReleaseHardware(GlobalsPtr globals);
  120. OSErr                    StartHardware(GlobalsPtr globals);
  121. void                    StopHardware(GlobalsPtr globals);
  122. void                    SuspendHardware(GlobalsPtr globals);
  123. void                    ResumeHardware(GlobalsPtr globals);
  124. OSErr                    SetHardwareVolume(GlobalsPtr globals, unsigned long volume);
  125. pascal void                InterruptRoutine(SndChannelPtr chan, SndCommand *cmd);
  126. SoundComponentDataPtr    GetMoreSource(GlobalsPtr globals);
  127. void                    CopySamplesToHardware(GlobalsPtr globals, SoundComponentDataPtr sourceDataPtr);
  128.  
  129.  
  130. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  131. // Main Component Entry Point
  132. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  133.  
  134. /*    =======================================================================================
  135.     NoiseMaker
  136.  
  137.     The function of this routine is to dispatch to the appropriate component method. It first
  138.     calls finds the address of the method to dispatch to using the selector provided in the
  139.     what field of the parameter block. If the address is -1L, then this selector should
  140.     be delegated. If the address is nil, this selector is not supported.
  141.     ======================================================================================= */
  142.  
  143. #ifdef THINK_C
  144. pascal ComponentResult main(ComponentParameters *params, GlobalsPtr globals)
  145. #else
  146. pascal ComponentResult NoiseMaker(ComponentParameters *params, GlobalsPtr globals)
  147. #endif
  148. {
  149.     ComponentRoutine    theRtn;
  150.     ComponentResult        result;
  151.  
  152.     theRtn = GetComponentRoutine(params->what);                // get address of component routine
  153.  
  154.     if (theRtn == nil)                                        // selector not implemented
  155.         result = badComponentSelector;
  156.     else if (theRtn == kDelegateComponentCall)                // selector should be delegated
  157.         result = DelegateComponentCall(params, globals->sourceComponent);
  158.     else
  159.         result = CallComponentFunctionWithStorage((Handle) globals, params, (ComponentFunctionUPP) theRtn);
  160.  
  161.     return (result);
  162. }
  163.  
  164. /*    =======================================================================================
  165.     GetComponentRoutine
  166.  
  167.     The function of this routine is to return the address of the appropriate component method.
  168.     To do this, the routine must deal with 3 selector ranges:
  169.  
  170.     -5 to -1    These are the standard Component Manager selectors that all components
  171.                 must share. Refer to the Component Manager documentation for more info.
  172.  
  173.     0 to 255    These selectors cannot be delegated. If the sound component does not implement
  174.                 one of these selectors, it should return the badComponentSelector error.
  175.  
  176.     256 to ∞    These selectors should be delegated. If the sound component does not implement
  177.                 one of these selectors, it should use DelegateComponentCall() to pass
  178.                 this selector on up the chain. If the sound component does implement this
  179.                 selector, it should first delegate the selector, then perform the function.
  180.     ======================================================================================= */
  181.  
  182. ComponentRoutine GetComponentRoutine(short selector)
  183. {
  184.     void     *theRtn;
  185.  
  186.     if (selector < 0)
  187.         switch (selector)                                    // standard component selectors
  188.         {
  189.             case kComponentRegisterSelect:
  190.                 theRtn = __ComponentRegister;
  191.                 break;
  192.  
  193.             case kComponentVersionSelect:
  194.                 theRtn = __ComponentVersion;
  195.                 break;
  196.  
  197.             case kComponentCanDoSelect:
  198.                 theRtn = __ComponentCanDo;
  199.                 break;
  200.  
  201.             case kComponentCloseSelect:
  202.                 theRtn = __ComponentClose;
  203.                 break;
  204.  
  205.             case kComponentOpenSelect:
  206.                 theRtn = __ComponentOpen;
  207.                 break;
  208.  
  209.             default:
  210.                 theRtn = nil;                                // unknown selector, so fail
  211.                 break;
  212.         }
  213.     else if (selector < kDelegatedSoundComponentSelectors)    // selectors that cannot be delegated
  214.         switch (selector)
  215.         {
  216.             case kSoundComponentInitOutputDeviceSelect:
  217.                 theRtn = __InitOutputDevice;
  218.                 break;
  219.  
  220.             default:
  221.                 theRtn = nil;                                // unknown selector, so fail
  222.                 break;
  223.         }
  224.     else                                                    // selectors that can be delegated
  225.         switch (selector)
  226.         {
  227.             case kSoundComponentGetInfoSelect:
  228.                 theRtn = __GetInfo;
  229.                 break;
  230.  
  231.             case kSoundComponentSetInfoSelect:
  232.                 theRtn = __SetInfo;
  233.                 break;
  234.  
  235.             case kSoundComponentStartSourceSelect:
  236.                 theRtn = __StartSource;
  237.                 break;
  238.  
  239.             case kSoundComponentPlaySourceBufferSelect:
  240.                 theRtn = __PlaySourceBuffer;
  241.                 break;
  242.  
  243.             default:
  244.                 theRtn = kDelegateComponentCall;            // unknown selector, so delegate it
  245.                 break;
  246.         }
  247.  
  248.     return (theRtn);
  249. }
  250.  
  251.  
  252. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  253. // Component Manager Methods
  254. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  255.  
  256.  
  257. /*    ==============================================================================
  258.     Component Register
  259.  
  260.     This routine is called once, usually at boot time, when the Component Manager
  261.     is first registering this sound component. This routine should check to see if the proper
  262.     hardware is installed and return 0 if it is. If the hardware is not installed,
  263.     the routine should return 1 and this component will not be registered. This is
  264.     also an opportunity to do one-time initializations and perhaps register this
  265.     component again if more than one hardware device is available. Global state information
  266.     can also be saved in the component refcon by calling SetComponentRefCon();
  267.  
  268.     NOTE: The cmpWantsRegisterMessage bit must be set in the component flags of the
  269.     sound component in order for this routine to be called.
  270.     NOTE: This routine is never called at interrupt time.
  271.     ============================================================================== */
  272.  
  273. pascal ComponentResult __ComponentRegister(void *unused1)
  274. {
  275. #pragma unused (unused1)
  276.  
  277.     long        result;
  278.     NumVersion    version;
  279.  
  280.     if ((Gestalt(gestaltSoundAttr, &result) == noErr) &&        // snd gestalt is available
  281.         (result & (1L << gestaltSoundIOMgrPresent)))            // snd dispatcher is available
  282.     {
  283.         version = SndSoundManagerVersion();                        // get the Sound Manager version
  284.         if (version.majorRev >= kRequiredSndMgrMajorRev)        // it's what we need
  285.         {
  286.  
  287.             //    Check for hardware here. We are always installed, so we return 0
  288.  
  289.             return (0);                                            // install this sound component
  290.         }
  291.     }
  292.     
  293.     return (1);                                                    // do not install component
  294. }
  295.  
  296. /*    ==============================================================================
  297.     Component Version
  298.  
  299.     This routine is called to determine the current version of the sound component.
  300.     It should return a fixed-point value containing the version, like 0x10001
  301.     for version 1.1. The version given here must match the version stored in
  302.     the 'thng' resource.
  303.  
  304.     NOTE: This routine is never called at interrupt time.
  305.     ============================================================================== */
  306.  
  307. pascal ComponentResult __ComponentVersion(void *unused1)
  308. {
  309. #pragma unused (unused1)
  310.  
  311.     return (kNoiseMakerVersion);                                // return sound component version
  312. }
  313.  
  314. /*    ==============================================================================
  315.     Component CanDo
  316.  
  317.     This routine is called to determine if a particular selector is implemented.
  318.     It should return 1 if this is a valid selector, or 0 if the selector is not
  319.     implemented or if the selector is always delegated.
  320.  
  321.     NOTE: This routine is never called at interrupt time.
  322.     ============================================================================== */
  323.  
  324. pascal ComponentResult __ComponentCanDo(void *unused1, short selector)
  325. {
  326. #pragma unused (unused1)
  327.  
  328.     ComponentRoutine    theRtn;
  329.  
  330.     theRtn = GetComponentRoutine(selector);                        // see if this selector is implemented
  331.  
  332.     if ((theRtn == nil) ||                                        // selector is not implemented
  333.         (theRtn == kDelegateComponentCall))                        // or selector is always delegated
  334.         return (0);                                                // no can do
  335.     else
  336.         return (1);                                                // selector is implemented
  337. }
  338.  
  339. /*    ==============================================================================
  340.     Component Open
  341.  
  342.     This routine is called when the Component Manager creates an instance of this
  343.     component. The routine should allocate global variables in the appropriate heap
  344.     and call SetComponentInstanceStorage() so the Component Manager can remember
  345.     the globals and pass them to all the method calls.
  346.     
  347.     Determining the heap to use can be tricky. The Component Manager will normally
  348.     load the component code into the system heap, which is good, since many applications
  349.     will be sharing this component to play sound. In this case, the components's global
  350.     variable storage should also be created in the system heap.
  351.  
  352.     However, if system heap memory is tight, the Component Manager will load
  353.     the component into the application heap of the first application that plays sound.
  354.     When this happens, the component should create global storage in the application heap
  355.     instead. The Sound Manager will make sure that other applications will not try
  356.     to play sound while the component is in this application heap.
  357.  
  358.     To determine the proper heap to use, call GetComponentInstanceA5(). If the value
  359.     returned is 0, then the component was loaded into the system heap, and all storage
  360.     should be allocated there. If the value returned is non-zero, the component is in
  361.     the application heap specifed by returned A5 value, and all storage should be
  362.     allocated in this application heap.
  363.     
  364.     NOTE: If the component is loaded into the application heap, the value returned by
  365.     GetComponentRefCon() will be 0.
  366.     NOTE: Do not attempt to initialize or access hardware in this call, since the
  367.     Component Manager will call Open() BEFORE calling Register().
  368.     Instead, initialize the hardware during InitOutputDevice(), described below.
  369.     NOTE: This routine is never called at interrupt time.
  370.     ============================================================================== */
  371.  
  372. pascal ComponentResult __ComponentOpen(void *unused1, ComponentInstance self)
  373. {
  374. #pragma unused (unused1)
  375.  
  376.     Handle            h;
  377.     GlobalsPtr        globals;
  378.     Boolean            inSystemHeap;
  379.  
  380.     inSystemHeap = GetComponentInstanceA5(self) == 0;        // find out if we were loaded in app heap or system heap
  381.  
  382.     h = NewHandleLockClear(sizeof(GlobalsRecord), inSystemHeap);    // get space for globals in appropriate heap
  383.     if (h == nil)
  384.         return(MemError());
  385.  
  386.     globals = (GlobalsPtr) *h;
  387.     SetComponentInstanceStorage (self, (Handle) globals);     // save pointer to our globals
  388.  
  389.     globals->self = self;                                    // remember ourselves
  390.     globals->globalsHandle = h;                                // remember the handle
  391.     globals->inSystemHeap = inSystemHeap;                    // remember which heap we are in
  392.     globals->prefsHandle = GetPreferences(self, inSystemHeap);    // retrieve or create preferences
  393.     if (globals->prefsHandle == nil)                        // could create preferences
  394.         return (memFullErr);                                // we can't run
  395.  
  396.     return (noErr);
  397. }
  398.  
  399. /*    ==============================================================================
  400.     Component Close
  401.  
  402.     This routine is called when the Component Manager is closing the instance of
  403.     this component. If the hardware was initialized, the routine should make sure
  404.     all remaining data is written to the hardware and that the hardware is completely
  405.     turned off. It should delete all global storage and close any other components
  406.     that were opened.
  407.     
  408.     NOTE: Be sure to check that the globals pointer passed in to this routine is
  409.     not set to NIL. If the Open() routine fails for any reason, the Component
  410.     Manager will call this routine passing in a NIL for the globals.
  411.     NOTE: This routine is never called at interrupt time.
  412.     ============================================================================== */
  413.  
  414. pascal ComponentResult __ComponentClose(GlobalsPtr globals, ComponentInstance self)
  415. {
  416.     if (globals)                                            // we have some globals
  417.     {
  418.         if (globals->hwInitialized)                            // hardware was initialized
  419.         {
  420.             ReleaseHardware(globals);                        // make sure the hardware is off and release it
  421.             HUnlock((Handle) globals->prefsHandle);            // let prefs handle roam now
  422.         }
  423.  
  424.         if (globals->sourceComponent)                        // we opened a mixer
  425.             CloseMixerSoundComponent(globals->sourceComponent);    // close it
  426.  
  427.         if (globals->prefsChanged)                            // preferences changed
  428.             SavePreferences(self, globals->prefsHandle);    // save them
  429.  
  430.         if (!globals->inSystemHeap)                            // prefs are in app heap
  431.             DisposeHandle((Handle) globals->prefsHandle);    // dispose of them
  432.     
  433.         DisposeHandle(globals->globalsHandle);                // dispose our storage
  434.     }
  435.  
  436.     return (noErr);
  437. }
  438.  
  439.  
  440. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  441. // Standard Sound Component Methods
  442. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  443.  
  444.  
  445. /*    ==============================================================================
  446.     InitOutputDevice
  447.  
  448.     This routine is called once when the Sound Manager first opens this sound component.
  449.     The routine should initialize the hardware to default values, allocate the
  450.     appropriate mixer and create any other memory that is required.
  451.  
  452.     NOTE: This routine is never called at interrupt time.
  453.     ============================================================================== */
  454.  
  455. pascal ComponentResult __InitOutputDevice(GlobalsPtr globals, long actions)
  456. {
  457. #pragma unused (actions)
  458.  
  459.     ComponentResult        result;
  460.     PreferencesPtr        prefsPtr;
  461.  
  462.     // Open the mixer and tell it the type of data it should output. The
  463.     // description includes sample format, sample rate, sample size, number of channels
  464.     // and the size of your optimal interrupt buffer. If a mixer cannot be found that
  465.     // will output this type of data, an error will be returned.
  466.  
  467.     prefsPtr = *globals->prefsHandle;                        // get settings from preferences
  468.  
  469.     // set to hardware defaults
  470.  
  471.     globals->hwSettings.flags = 0;
  472.     globals->hwSettings.format = (prefsPtr->sampleSize == 8) ? kOffsetBinary : kTwosComplement;
  473.     globals->hwSettings.sampleRate = prefsPtr->sampleRate;
  474.     globals->hwSettings.sampleSize = prefsPtr->sampleSize;
  475.     globals->hwSettings.numChannels = prefsPtr->numChannels;
  476.     globals->hwSettings.sampleCount = kInterruptBufferSamples * 2;
  477.  
  478.     // open mixer that will output this format
  479.     
  480.     result = OpenMixerSoundComponent(&globals->hwSettings, 0, &globals->sourceComponent);
  481.     if (result != noErr)
  482.         return (result);
  483.  
  484.     result = SetupHardware(globals);                        // setup the hardware to these settings
  485.  
  486.     if (result == noErr)
  487.     {
  488.         globals->hwInitialized = true;                        // hardware is ready to go
  489.         HLock((Handle) globals->prefsHandle);                // lock prefs so we can use them at interrupt time
  490.     }
  491.  
  492.     return (result);
  493. }
  494.  
  495. /*    ==============================================================================
  496.     GetInfo
  497.  
  498.     This routine returns information about this output component to the Sound Manager.
  499.     A 4-byte OSType selector is used to determine the type and size of the information
  500.     to return. If the component does not support a selector, it should delegate this
  501.     call on up the chain.
  502.  
  503.     NOTE: This can be called at interrupt time. However, selectors that return
  504.     a handle will not be called at interrupt time.
  505.     ============================================================================== */
  506.  
  507. pascal ComponentResult __GetInfo(GlobalsPtr globals, SoundSource sourceID,
  508.                                  OSType selector, void *infoPtr)
  509. {
  510.     HandleListPtr        listPtr;
  511.     short                *sp;
  512.     UnsignedFixed        *lp;
  513.     Handle                h;
  514.     PreferencesPtr        prefsPtr;
  515.     ComponentResult        result = noErr;
  516.  
  517.     prefsPtr = *globals->prefsHandle;                    // get settings from preferences
  518.  
  519.     switch (selector)
  520.     {
  521.         case siSampleSize:                                // return current sample size
  522.             *((short *) infoPtr) = prefsPtr->sampleSize;
  523.             break;
  524.  
  525.         case siSampleSizeAvailable:                        // return samples sizes available
  526.             h = NewHandle(sizeof(short) * kSampleSizesCount);    // space for sample sizes
  527.             if (h == nil)
  528.                 return (MemError());
  529.  
  530.             listPtr = (HandleListPtr) infoPtr;
  531.             listPtr->count = kSampleSizesCount;            // no. sample sizes in handle
  532.             listPtr->handle = h;                        // handle to be returned
  533.  
  534.             sp = (short *) *h;                            // store sample sizes in handle
  535.             *sp++ = k8BitSamples;
  536.             *sp++ = k16BitSamples;
  537.             break;
  538.  
  539.         case siSampleRate:                                // return current sample rate
  540.             *((Fixed *) infoPtr) = prefsPtr->sampleRate;
  541.             break;
  542.  
  543.         case siSampleRateAvailable:                        // return sample rates available
  544.             h = NewHandle(sizeof(UnsignedFixed) * kSampleRatesCount);    // space for sample rates
  545.             if (h == nil)
  546.                 return (MemError());
  547.  
  548.             listPtr = (HandleListPtr) infoPtr;
  549.             listPtr->count = kSampleRatesCount;            // no. sample rates in handle
  550.             listPtr->handle = h;                        // handle to be returned
  551.  
  552.             lp = (UnsignedFixed *) *h;
  553.  
  554.             // If the hardware can support a range of sample rate values, then the
  555.             // list count should be set to zero and the min and max sample rate values
  556.             // should be stored in the handle.
  557.  
  558.             if (kSupportsSampleRateRange)
  559.             {
  560.                 listPtr->count = 0;
  561.                 *lp++ = kSampleRateMin;                    // min
  562.                 *lp++ = kSampleRateMax;                    // max
  563.             }
  564.             
  565.             // If the hardware supports a limited set of sample rates, then the list count
  566.             // should be set to the number of sample rates and this list of rates should be
  567.             // stored in the handle.
  568.  
  569.             else
  570.             {
  571.                 *lp++ = kSampleRate11;                    // store sample rates in handle
  572.                 *lp++ = kSampleRate22;
  573.                 *lp++ = kSampleRate44;
  574.             }
  575.             break;
  576.  
  577.         case siNumberChannels:                            // return current no. channels
  578.             *((short *) infoPtr) = prefsPtr->numChannels;
  579.             break;
  580.  
  581.         case siChannelAvailable:                        // return channels available
  582.             h = NewHandle(sizeof(short) * kChannelsCount);    // space for channels
  583.             if (h == nil)
  584.                 return (MemError());
  585.  
  586.             listPtr = (HandleListPtr) infoPtr;
  587.             listPtr->count = kChannelsCount;            // no. channels in handle
  588.             listPtr->handle = h;                        // handle to be returned
  589.  
  590.             sp = (short *) *h;                            // store channels in handle
  591.             *sp++ = kNumChannelsMono;
  592.             *sp++ = kNumChannelsStereo;
  593.             break;
  594.  
  595.         case siHardwareVolume:
  596.             *((long *)infoPtr) = prefsPtr->volume;
  597.             break;
  598.  
  599.         // if you do not handle this selector, then delegate it up the chain
  600.         default:
  601.             result = SoundComponentGetInfo(globals->sourceComponent, sourceID, selector, infoPtr);
  602.             break;
  603.  
  604.     }
  605.  
  606.     return (result);
  607. }
  608.  
  609. /*    ==============================================================================
  610.     SetInfo
  611.  
  612.     This routine sets information about this component. A 4-byte OSType selector is
  613.     used to determine the type and size of the information to apply. If the component
  614.     does not support a selector, it should delegate this call on up the chain.
  615.  
  616.     NOTE: This can be called at interrupt time.
  617.     ============================================================================== */
  618.  
  619. pascal ComponentResult __SetInfo(GlobalsPtr globals, SoundSource sourceID,
  620.                                  OSType selector, void *infoPtr)
  621. {
  622.     PreferencesPtr        prefsPtr;
  623.     ComponentResult        result = noErr;
  624.  
  625.     prefsPtr = *globals->prefsHandle;                    // get settings from preferences
  626.  
  627.     switch (selector)
  628.     {
  629.         case siSampleSize:                                // set sample size
  630.         {
  631.             short    sampleSize = (short) infoPtr;
  632.             
  633.             if ((sampleSize == k8BitSamples) ||            // make sure it is a valid sample size
  634.                 (sampleSize == k16BitSamples))
  635.             {
  636.                 prefsPtr->sampleSize = sampleSize;        // save new size in prefs
  637.                 globals->prefsChanged = true;            // save prefs on close
  638.             }
  639.             else
  640.                 result = siInvalidSampleSize;
  641.             break;
  642.         }
  643.  
  644.         case siSampleRate:                                // set sample rate
  645.         {
  646.             UnsignedFixed    sampleRate = (UnsignedFixed) infoPtr;
  647.  
  648.             if (kSupportsSampleRateRange)                // sample rate range
  649.             {
  650.                 if ((kSampleRateMin <= sampleRate) && (sampleRate <= kSampleRateMax))    // make sure it is a valid sample rate
  651.                 {
  652.                     prefsPtr->sampleRate = sampleRate;    // save new rate in prefs
  653.                     globals->prefsChanged = true;        // save prefs on close
  654.                 }
  655.                 else
  656.                     result = siInvalidSampleRate;
  657.             }
  658.             else
  659.             {
  660.                 if ((sampleRate == kSampleRate11) ||    // make sure it is a valid sample rate
  661.                     (sampleRate == kSampleRate22) ||
  662.                     (sampleRate == kSampleRate44))
  663.                 {
  664.                     prefsPtr->sampleRate = sampleRate;    // save new rate in prefs
  665.                     globals->prefsChanged = true;        // save prefs on close
  666.                 }
  667.                 else
  668.                     result = siInvalidSampleSize;
  669.             }
  670.             break;
  671.         }
  672.  
  673.         case siNumberChannels:                            // set no. channels
  674.         {
  675.             short    numChannels = (short) infoPtr;
  676.  
  677.             if ((numChannels == kNumChannelsMono) ||    // make sure it is a valid no. channels
  678.                 (numChannels == kNumChannelsStereo))
  679.             {
  680.                 prefsPtr->numChannels = numChannels;    // save new num channels in prefs
  681.                 globals->prefsChanged = true;            // save prefs on close
  682.             }
  683.             else
  684.                 result = notEnoughHardware;
  685.             break;
  686.         }
  687.  
  688.         case siHardwareVolume:
  689.             prefsPtr->volume = (long) infoPtr;            // save volume in prefs
  690.             globals->prefsChanged = true;                // save prefs on close
  691.             result = SetHardwareVolume(globals, prefsPtr->volume);
  692.             break;
  693.  
  694.         // if you do not handle this selector, then call up the chain
  695.         default:
  696.             result = SoundComponentSetInfo(globals->sourceComponent, sourceID, selector, infoPtr);
  697.             break;
  698.     }
  699.  
  700.     return (result);
  701. }
  702.  
  703. /*    ==============================================================================
  704.     StartSource
  705.  
  706.     This routine is used to start sounds playing that are currently paused. It should
  707.     first delegate this call up the chain so the rest of the chain can prepare
  708.     to play this sound. Then, if the hardware is not already started it should be
  709.     turned on.
  710.  
  711.     NOTE: This can be called at interrupt time.
  712.     ============================================================================== */
  713.  
  714. pascal ComponentResult __StartSource(GlobalsPtr globals, short count, SoundSource *sources)
  715. {
  716.     ComponentResult        result;
  717.  
  718.     // tell the mixer to start these sources
  719.     result = SoundComponentStartSource(globals->sourceComponent, count, sources);
  720.     if (result != noErr)
  721.         return (result);
  722.  
  723.     // make sure hardware interrupts are running
  724.     result = StartHardware(globals);
  725.  
  726.     return (result);
  727. }
  728.  
  729. /*    ==============================================================================
  730.     PlaySourceBuffer
  731.  
  732.     This routine is used to specify a new sound to play and conditionally start
  733.     the hardware playing that sound.It should first delegate this call up the
  734.     chain so the rest of the chain can prepare to play this sound. Then, if the
  735.     hardware is not already started it should be turned on.
  736.  
  737.     NOTE: This can be called at interrupt time.
  738.     ============================================================================== */
  739.  
  740. pascal ComponentResult __PlaySourceBuffer(GlobalsPtr globals, SoundSource sourceID, SoundParamBlockPtr pb, long actions)
  741. {
  742.     ComponentResult        result;
  743.  
  744.     // tell mixer to start playing this new buffer
  745.     result = SoundComponentPlaySourceBuffer(globals->sourceComponent, sourceID, pb, actions);
  746.     if (result != noErr)
  747.         return (result);
  748.  
  749.     // if the kSourcePaused bit is set, then do not turn on your hardware just yet
  750.     // (the assumption is that StartSource() will later be used to start this sound playing).
  751.     // If this bit is not set, turn your hardware interrupts on.
  752.  
  753.     if (!(actions & kSourcePaused))
  754.         result = StartHardware(globals);
  755.  
  756.     return (result);
  757. }
  758.  
  759.  
  760. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  761. // This routine creates a locked handle in the requested heap.
  762.  
  763. Handle NewHandleLockClear(long len, Boolean inSystemHeap)    // allocate a new handle, lock it down and clear it
  764. {
  765.     Handle        h;
  766.  
  767.     if (inSystemHeap)                                        // we are loaded into the system heap
  768.     {
  769.         ReserveMemSys(len);                                    // create it low down in system heap
  770.         h = NewHandleSysClear(len);
  771.     }
  772.     else                                                    // we are loaded into the app heap
  773.     {
  774.         h = NewHandleClear(len);                            // create our memory there and move it out of the way
  775.         if (h) MoveHHi(h);
  776.     }
  777.  
  778.     if (h) HLock(h);                                        // lock it down
  779.     return (h);
  780. }
  781.  
  782. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  783. // This routine gets the preferences from the refCon or the preferences file
  784. // or creates default preferences if it has to.
  785.  
  786. PreferencesHandle GetPreferences(ComponentInstance self, Boolean inSystemHeap)
  787. {
  788.     Handle                    prefsHandle, componentName;
  789.     PreferencesPtr            prefsPtr;
  790.     ComponentDescription    componentDesc;
  791.     OSErr                    err;
  792.  
  793.     prefsHandle = (Handle) GetComponentRefcon((Component) self);    // get prefs from component refcon
  794.     if (prefsHandle)
  795.         return ((PreferencesHandle) prefsHandle);            // valid prefs in refcon, so we are done
  796.  
  797.     componentName = NewHandle(0);                            // make space for name
  798.     if (componentName == nil)
  799.         goto NewNameHandleFailed;
  800.  
  801.     err = GetComponentInfo((Component) self, &componentDesc, componentName, nil, nil);    // get component name and subtype
  802.     if (err != noErr)
  803.         goto InfoFailed;
  804.  
  805.     prefsHandle = NewHandleLockClear(sizeof(PreferencesRecord), inSystemHeap);    // create space for prefs handle
  806.     if (prefsHandle == nil)
  807.         goto NewPrefsHandleFailed;
  808.  
  809.     HUnlock(prefsHandle);                                    // don't leave prefs handle locked down forever
  810.     HLock(componentName);
  811.  
  812.     err = GetSoundPreference(componentDesc.componentSubType, (StringPtr) *componentName, prefsHandle);    // get prefs handle from file
  813.     if (err != noErr)                                        // no file or prefs not in file
  814.         goto GetPrefsFailed;
  815.  
  816.     if (GetHandleSize(prefsHandle) != sizeof(PreferencesRecord))    // older version of preferences
  817.         goto GetPrefsFailed;                                // start with all new preferences
  818.  
  819.     DisposeHandle(componentName);
  820.     SetComponentRefcon((Component) self, (long) prefsHandle);    // save prefs in refcon
  821.  
  822.     return ((PreferencesHandle) prefsHandle);                // we are done
  823.  
  824.  
  825.     /*    If we end up here, it means that the preferences could not be loaded out of the
  826.         sound preferences file for some reason, so we need to generate some default preferences. */
  827.  
  828. GetPrefsFailed:
  829.     DisposeHandle(prefsHandle);
  830. NewPrefsHandleFailed:
  831. InfoFailed:
  832.     DisposeHandle(componentName);
  833. NewNameHandleFailed:
  834.  
  835.     prefsHandle = NewHandleLockClear(sizeof(PreferencesRecord), inSystemHeap);    // create space for prefs handle
  836.     if (prefsHandle == nil)
  837.         return (nil);
  838.  
  839.     HUnlock(prefsHandle);                                    // don't leave prefs handle locked down forever
  840.     prefsPtr = (PreferencesPtr) *prefsHandle;
  841.  
  842.     prefsPtr->sampleSize = k8BitSamples;                    // initialize prefs to defaults
  843.     prefsPtr->sampleRate = kSampleRate22;
  844.     prefsPtr->numChannels = kNumChannelsStereo;
  845.     prefsPtr->volume = (kFullVolume << 16) | kFullVolume;
  846.  
  847.     SetComponentRefcon((Component) self, (long) prefsHandle);    // save prefs in refcon
  848.  
  849.     return ((PreferencesHandle) prefsHandle);                // we are done
  850. }
  851.  
  852. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  853. // This routine saves the hardware globals in the preferences file
  854.  
  855. void SavePreferences(ComponentInstance self, PreferencesHandle prefsHandle)
  856. {
  857.     Handle                    componentName;
  858.     ComponentDescription    componentDesc;
  859.     OSErr                    err;
  860.  
  861.     componentName = NewHandle(0);                            // make space for name
  862.     if (componentName == nil)
  863.         goto NewNameHandleFailed;
  864.  
  865.     err = GetComponentInfo((Component) self, &componentDesc, componentName, nil, nil);    // get component name and subtype
  866.     if (err != noErr)
  867.         goto InfoFailed;
  868.  
  869.     HLock(componentName);
  870.     err = SetSoundPreference(componentDesc.componentSubType, (StringPtr) *componentName, (Handle) prefsHandle);
  871.  
  872. InfoFailed:
  873.     DisposeHandle(componentName);
  874. NewNameHandleFailed:
  875.  
  876.     return;
  877. }
  878.  
  879.  
  880. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  881. // Hardware-specific Methods
  882. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  883.  
  884.  
  885. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  886. // Initialize the hardware. Our "hardware" just uses the Sound Manager to
  887. // play sounds. Your hardware would probably do something completely different.
  888. //
  889. // We have to find another output component to play sound with, or the Sound Manager
  890. // will try to use our component by default when we call SndNewChannel. We do this
  891. // by searching for another output component, and passing that component to SndNewChannel
  892. // with a flag set telling the Sound Manager to use this component instead of the default. 
  893.  
  894.  
  895. OSErr SetupHardware(GlobalsPtr globals)
  896. {
  897.     ComponentDescription    searching, found;
  898.     OSType                    selfSubType;
  899.     Component                sndComponent;
  900.     OSErr                    err;
  901.  
  902.     // find our subtype
  903.  
  904.     err = GetComponentInfo((Component) globals->self, &found, nil, nil, nil);    // get component subtype
  905.     if (err != noErr)
  906.         return (err);
  907.  
  908.     selfSubType = found.componentSubType;                        // save it for later
  909.  
  910.     // search for another output component that can play sounds
  911.  
  912.     searching.componentType = kSoundHardwareType;
  913.     searching.componentSubType = kAnyComponentSubType;
  914.     searching.componentManufacturer = kAnyComponentManufacturer;
  915.     searching.componentFlags = 0;
  916.     searching.componentFlagsMask = kAnyComponentFlagsMask;
  917.  
  918.     sndComponent = nil;
  919.     while (sndComponent = FindNextComponent(sndComponent, &searching))
  920.     {
  921.         err = GetComponentInfo(sndComponent, &found, nil, nil, nil);    // get component subtype
  922.         if (err != noErr)
  923.             return (err);
  924.  
  925.         if (found.componentSubType != selfSubType)                // found something besides ourself
  926.             break;
  927.     }
  928.  
  929.     if (sndComponent == nil)                                    // no other output components available
  930.         return (notEnoughHardware);                                // bail
  931.  
  932.     // play sounds out this component
  933.  
  934.     globals->sndChan = nil;
  935.     err = SndNewChannel(&globals->sndChan, kUseOptionalOutputDevice, (long) sndComponent, InterruptRoutine);
  936.  
  937.     return (err);
  938. }
  939.  
  940. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  941. // Release the hardware. 
  942.  
  943. void ReleaseHardware(GlobalsPtr globals)
  944. {
  945.     StopHardware(globals);                                        // make sure hardware is off
  946.  
  947.     SndDisposeChannel(globals->sndChan, true);                    // release sound channel
  948. }
  949.  
  950. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  951. // Turn on hardware interrupts.
  952.  
  953. OSErr StartHardware(GlobalsPtr globals)
  954. {
  955.     OSErr    err = noErr;
  956.  
  957.     if (!globals->hwInterruptsOn)
  958.     {
  959.         SndCommand    cmd;
  960.  
  961.         globals->hwInterruptsOn = true;                            // the hardware will soon be on
  962.  
  963.         // Turn hardware on here.
  964.  
  965.         cmd.cmd = callBackCmd;                                    // we just queue up a callback to start interrupts
  966.         cmd.param1 = 0;
  967.         cmd.param2 = (long) globals;
  968.     
  969.         err = SndDoCommand(globals->sndChan, &cmd, true);        // call the interrupt routine
  970.     }
  971.  
  972.     return (err);
  973. }
  974.  
  975. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  976. // Turn off hardware interrupts.
  977.  
  978. void StopHardware(GlobalsPtr globals)
  979. {
  980.     if (globals->hwInterruptsOn)
  981.     {
  982.         SndCommand    cmd;
  983.  
  984.         // Turn hardware off here.
  985.  
  986.         cmd.cmd = quietCmd;
  987.         cmd.param1 = 0;
  988.         cmd.param2 = 0;
  989.     
  990.         SndDoImmediate(globals->sndChan, &cmd);                    // stop sound playback
  991.  
  992.         globals->hwInterruptsOn = false;                        // the hardware is now off
  993.     }
  994. }
  995.  
  996. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  997. // Temporarily suspend hardware interrupts, so the interrupt routine can work in peace.
  998.  
  999. void SuspendHardware(GlobalsPtr globals)
  1000. {
  1001.     if (globals->hwInterruptsOn)
  1002.     {
  1003.         // Suspend hardware interrupts here
  1004.     }
  1005. }
  1006.  
  1007. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1008. // Resume hardware interrupts after they were suspended.
  1009.  
  1010. void ResumeHardware(GlobalsPtr globals)
  1011. {
  1012.     if (globals->hwInterruptsOn)
  1013.     {
  1014.         SndCommand    cmd;
  1015.  
  1016.         // Turn hardware on here.
  1017.  
  1018.         cmd.cmd = callBackCmd;                                    // we just queue up a callback to resume interrupts
  1019.         cmd.param1 = 0;
  1020.         cmd.param2 = (long) globals;
  1021.     
  1022.         SndDoCommand(globals->sndChan, &cmd, true);                // call the interrupt routine
  1023.     }
  1024. }
  1025.  
  1026. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1027. // Set hardware volume.
  1028.  
  1029. OSErr SetHardwareVolume(GlobalsPtr globals, unsigned long volume)
  1030. {
  1031.     SndCommand    cmd;
  1032.     OSErr        err;
  1033.  
  1034.     cmd.cmd = volumeCmd;
  1035.     cmd.param1 = 0;
  1036.     cmd.param2 = volume;
  1037.  
  1038.     err = SndDoImmediate(globals->sndChan, &cmd);            // set volume for this channel
  1039.     
  1040.     return (err);
  1041. }
  1042.  
  1043. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1044. // This routine is called every hardware interrupt to fill the hardware
  1045. // with audio data. First it should suspend interrupts so it will not
  1046. // be interrupted again. Then it should get more data from the source mixer,
  1047. // and copy the data to the hardware. On the way out, if all data was copied,
  1048. // try to get some more so it will be available immediately next interrupt.
  1049.  
  1050. pascal void InterruptRoutine(SndChannelPtr chan, SndCommand *cmd)
  1051. {    
  1052. #pragma unused (chan)
  1053.  
  1054.     GlobalsPtr                globals;
  1055.     SoundComponentDataPtr    sourceDataPtr;
  1056.  
  1057.     globals = (GlobalsPtr) cmd->param2;                        // get globals from command
  1058.  
  1059.     SuspendHardware(globals);                                // suspend interrupts while we are processing
  1060.  
  1061.     sourceDataPtr = GetMoreSource(globals);                    // get source from mixer
  1062.     if (sourceDataPtr == nil)                                // no more source
  1063.     {
  1064.         StopHardware(globals);                                // turn hardware off
  1065.         return;
  1066.     }
  1067.  
  1068.     CopySamplesToHardware(globals, sourceDataPtr);            // fulfill hardware request
  1069.  
  1070. //    Normally, you will want to check to see if you have run out
  1071. //     of data here and get more right away so you will be ready for
  1072. //    the next interrupt. This example does not have any hardware
  1073. //    to copy the data to, so it leaves the data in the mixer buffer and
  1074. //    thus cannot call for more until it has been played.
  1075.  
  1076. //    if (sourceDataPtr->sampleCount == 0)                    // exhausted the source
  1077. //        sourceDataPtr = GetMoreSource(globals);                // get more for next time
  1078.  
  1079.     ResumeHardware(globals);                                // resume interrupts
  1080. }
  1081.  
  1082. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1083. // This routine returns the data pointer to your mixer source. If there
  1084. // is no source or it is empty, it will call down the chain to fill it up.
  1085.  
  1086. SoundComponentDataPtr GetMoreSource(GlobalsPtr globals)
  1087. {
  1088.     ComponentResult            result;
  1089.     SoundComponentDataPtr    sourceDataPtr = globals->sourceDataPtr;
  1090.  
  1091.     if ((sourceDataPtr == nil) || (sourceDataPtr->sampleCount == 0))    // no data - better get some
  1092.     {
  1093.         result = SoundComponentGetSourceData(globals->sourceComponent, &globals->sourceDataPtr);
  1094.         sourceDataPtr = globals->sourceDataPtr;
  1095.  
  1096.         if ((result != noErr) ||                            // error getting data
  1097.             (sourceDataPtr == nil) ||                        // source has no data pointer to return
  1098.             (sourceDataPtr->sampleCount == 0))                // source has no more data
  1099.         {
  1100.             return (nil);                                    // stop the presses
  1101.         }
  1102.     }
  1103.     
  1104.     return (sourceDataPtr);                                    // return pointer to source
  1105. }
  1106.  
  1107.  
  1108. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1109. // Example-specific Methods
  1110. //
  1111. // These routines are only needed to make this example work and as such should not be
  1112. // used in your sound component.
  1113. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1114.  
  1115.  
  1116. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1117. // This routine copies the data returned by the mixer to the hardware.
  1118.  
  1119. void CopySamplesToHardware(GlobalsPtr globals, SoundComponentDataPtr sourceDataPtr)
  1120. {
  1121.     SndCommand        cmd;
  1122.     ExtSoundHeader    sndHeader;
  1123.     OSErr            err;
  1124.  
  1125.     sndHeader.samplePtr = (Ptr) sourceDataPtr->buffer;        // buffer to play
  1126.     sndHeader.numChannels = sourceDataPtr->numChannels;        // number of channels
  1127.     sndHeader.sampleRate = sourceDataPtr->sampleRate;        // sample rate
  1128.     sndHeader.loopStart = 0;                                // no loop points
  1129.     sndHeader.loopEnd = 0;
  1130.     sndHeader.encode = extSH;                                // this is an extended header
  1131.     sndHeader.baseFrequency = 60;                            // base note
  1132.     sndHeader.numFrames = sourceDataPtr->sampleCount;        // length in samples
  1133.     sndHeader.markerChunk = 0;                                // sync track
  1134.     sndHeader.instrumentChunks = 0;                            // AIFF instrument chunks
  1135.     sndHeader.AESRecording = 0;
  1136.     sndHeader.sampleSize = sourceDataPtr->sampleSize;        // number of bits in sample
  1137.     sndHeader.futureUse1 = 0;                                // reserved by Apple
  1138.     sndHeader.futureUse2 = 0;                                // reserved by Apple
  1139.     sndHeader.futureUse3 = 0;                                // reserved by Apple
  1140.     sndHeader.futureUse4 = 0;                                // reserved by Apple
  1141.  
  1142.     cmd.cmd = bufferCmd;
  1143.     cmd.param1 = 0;
  1144.     cmd.param2 = (long) &sndHeader;
  1145.  
  1146.     err = SndDoImmediate(globals->sndChan, &cmd);            // play this sound
  1147.  
  1148.     sourceDataPtr->sampleCount = 0;                            // sound has been played
  1149. }
  1150.